/*
 *  Yatun  - Yet another tunneling application
 *
 *  Michal Demin - 2007
 *
 *  ConfigParser.cpp 
 *  Configuration file parser
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#include "ConfigParser.h"

#undef DEBUG

ConfigParser::ConfigParser ()
{
  filename = NULL;
  secName = NULL;
  Properties = NULL;
}

ConfigParser::~ConfigParser ()
{
  Close ();
}

/* 
 * Uzatvory subor a odalokuje pamet
 */
void
ConfigParser::Close ()
{
  fclose (fd);
  destroyProperties ();
  free (filename);
  free (secName);
}

/*
 * Otvori zadany subor na parsovanie
 */
int
ConfigParser::Open (const char *name)
{
  filename = strdup (name);
  fd = fopen (name, "r");
  if (fd == 0) {
    printf ("Unable to open config file \n");
    return -1;
  }
  return 0;
}

/*
 * Najde pozadovanu sekciu v konfiguracnom subore a nacita jednotlive parametre do linearneho zoznamu
 */
int
ConfigParser::GetSection (const char *section)
{
  int secNameLen = strlen (section);
  char *line = NULL;
  int lineLen = 0;
  int poz = 0;			// pozicia '=' na riadku 
  t_Property *First = NULL, *Last = NULL;

  if ((secName != NULL) && (strcmp (secName, section) == 0)) {	// uz je nacitana , tak konec 
    return 0;
  }
  
  if (secName != NULL) {
    free (secName);
  }
  secName = strdup (section);

  destroyProperties ();
  fseek (fd, 0, SEEK_SET);
  while (1) { // najdem meno sekcie v subore 
    if (line != NULL) {
      delete [] line;

      line = NULL;
    }
    line = getNextLine ();
    if (line == NULL) {
      return -1;
    }
    trimWhitespace (line);
    if (line[0] != '[') {
#ifdef DEBUG
      printf ("contin \n");
#endif
      continue;
    }
#ifdef DEBUG
    printf ("sekcia  %s\n", line + secNameLen);
#endif
    if ((strncmp (line + 1, section, secNameLen) == 0) && line[secNameLen + 1] == ']') {	// nasiel som sekciu :) ideme dalej
#ifdef DEBUG
      printf ("najdena sekcia \n");
#endif
      break;
    }
  }
  
  // ak som sa dostal sem tak sme asi nasli pozadovanu sekciu
  // teraz potrebujem vsetky riadky ktore nezacinaju '[' a obsahuju '='

  delete [] line;

  while ((line = getNextLine ()) != NULL) {
    trimWhitespace (line);
    lineLen = strlen (line);

    if (lineLen == 0) {
      delete [] line;
      line = NULL;
      continue;
    }
    if (line[0] == '[') {
      delete [] line;
      line = NULL;
      break;			// koniec sekcie
    }
#ifdef DEBUG
    printf ("line(%d) = %s\n", lineLen, line);
#endif
    poz = findChar (line, lineLen, '=');
    if (poz > 0) {		// parsnem a pridam do linearneho zoznama...
      line[poz] = '\0';
      t_Property *newTemp = new t_Property;

      newTemp->Name = strdup (line);
      newTemp->Value = strdup (line + poz + 1);
      newTemp->Next = NULL;

      if (First == NULL) {
	First = newTemp;
	Last = First;
      } else {
	Last->Next = newTemp;
	Last = Last->Next;
      }
    }
    delete [] line;
    line = NULL;
  }
    
  if(line) {
    delete [] line;
  }
  
  Properties = First;
  return 0;
}

/*
 * Vrati pointer na strukturu zadaneho parametru (ak existuje v subore)
 */
t_Property *
ConfigParser::GetProperty (const char *inName)
{
  t_Property *tmp = Properties;

  while (tmp) { // prebehneme linearny zoznam a ak prvok existuje tak ho vrat 
    if (strcmp (tmp->Name, inName) == 0)
      break;
    tmp = tmp->Next;
  }

  return tmp;
}

/*
 * Zapise hodnotu parametru zadaneho menom do buffru, ktoreho typ je zadany v premennej "type"
 */
int
ConfigParser::GetPropertyByType (const char *inName, void *buf, char type)
{
  t_Property *tmp;

  tmp = GetProperty (inName);

  float f_val = 0.00;
  int i_val = 0;
  int b_val = 0;
  char *end_ptr = NULL;
  int no = 0;

  if (tmp == NULL) {
    return -1;
  }
  // mame nacitane spravime konverziu
  errno = 0;
  switch (type) {
  case T_FLOAT:
    f_val = strtof (tmp->Value, &end_ptr);
    no = errno;
    if (no != 0) {
      printf ("Conversion error: %s\n", strerror (no));
      return -1;
    }
    if ((tmp->Value == end_ptr) || (*end_ptr != '\0')) {
      printf ("Conversion error: invalid input format !\n");
      return -1;
    }
    *((float *) buf) = f_val;
    break;

  case T_INT:
    i_val = strtol (tmp->Value, &end_ptr, 10);
    no = errno;
    if (no != 0) {
      printf ("Conversion error: %s\n", strerror (no));
      return -1;
    }
    if ((tmp->Value == end_ptr) || (*end_ptr != '\0')) {
      printf ("Conversion error: invalid input format !\n");
      return -1;
    }
    *((int *) buf) = i_val;
    break;

  case T_BOOL:
    if (tmp->Value[0] == 'f' || tmp->Value[0] == 'F' || tmp->Value[0] == '0')
      b_val = 0;
    else
      b_val = 1;
    *((int *) buf) = b_val;
    break;

  case T_STRING:
    strcpy ((char *) buf, tmp->Value);
    break;

  default:
    printf ("invalid type specified \n");
    return -1;

  }

  return 0;
}

/*
 * Funkcia nacita dalsi riadok zo suboru
 */
char *
ConfigParser::getNextLine ()
{
  int ch = 0;
  char *tmp = NULL;
  char *out = NULL;
  int outLen = 0;
  int index = 0;
  int in_comment = 0;

  tmp = new char[LINE_SIZE];

  memset (tmp, 0, LINE_SIZE);

  ch = getc (fd);
  if (ch == EOF) {
    delete[]tmp;
    return NULL;
  }
  // nacitavaj az po koniec riadku, alebo suboru
  while ((((char) ch) != '\n') && (ch != EOF)) {
    if ((char) ch == '#') {
      // zaciatok komentaru ... tak nezapisuj znaky ... 
      in_comment = 1;
    }

    if (!in_comment)
      tmp[index++] = (char) ch; //nacitavam po znaku, ak najdem komentar tak ten ignorujem
    ch = getc (fd);
  }

  trimWhitespace (tmp);
//  outLen = strlen (tmp);
//  out = new char[outLen];
  
  memcpy (out, tmp, outLen);
  return tmp;
}

/*
 * Funkcia vyhodi biele znaky z retazca
 */
char *
ConfigParser::trimWhitespace (char *input)
{
  char inString = '\0';
  int sIndex = 0;
  int dIndex = 0;
  int Len = 0;

  if (input == NULL)
    return NULL;

  Len = strlen (input);

  if (Len == 0)
    return NULL;

  while (input[sIndex] != '\0') {
    if ((inString == '\0') && ((input[sIndex] == '"') || (input[sIndex] == '\''))) {

      inString = input[sIndex];
    } else if ((inString != '\0') && (input[sIndex] == inString)) {
      inString = '\0';
    }

    if ((inString == '\0') && ((input[sIndex] == '\t') ||	// isblank ?
			       (input[sIndex] == '\n') ||
			       (input[sIndex] == ' ') ||
			       (input[sIndex] == '\r') || (input[sIndex] == '\v'))) {
			       
      sIndex++;
      continue;
    }
    input[dIndex] = input[sIndex];
    sIndex++;
    dIndex++;
  }
  input[dIndex] = '\0';
  return input;
}

int
ConfigParser::findChar (char *buf, uint siz, char ch)
{
  char *p;
  uint x;

  if (!buf)
    return -2;

  p = buf;

  for (x = 0; x < siz; x++) {
    if (*p == ch) {
      x = (int) (p - buf);
      return (x);
    }
    p++;
  }				/* for */
  return -1;
}

/*
 *  Zmaze linearny zoznam parametrov
 */
void
ConfigParser::destroyProperties ()
{
  t_Property *tmp;

  while (Properties) {
    free (Properties->Name);
    free (Properties->Value);
    tmp = Properties;
    Properties = Properties->Next;
    delete tmp;
  }
}
